package network;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Ap0stoL2
 * Date: 2023-08-09
 * Time: 21:43
 */

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

// UDP 的 回显服务器.
// 客户端发的请求是啥, 服务器返回的响应就是啥.
public class UdpEchoServer {
    //数据报对象
    private DatagramSocket socket = null;


    // 参数是服务器要绑定的端口,构造方法,port时端口号
    public UdpEchoServer(int port) throws SocketException {//网络编程一个常见的异常
        socket = new DatagramSocket(port); //端口号占用了则会new异常
    }


    // 使用这个方法启动服务器.
    public void start() throws IOException {//网络通信也是一种IO也会抛出IOException
        System.out.println("服务器启动!");//日志,证明启动了

        //一个服务器要给很多客户端提供服务
        //一个服务器不知道客户端啥时候来,所以反复的, 长期的执行针对客户端请求处理的逻辑(while)
        while (true) {
            // 反复的, 长期的执行针对客户端请求处理的逻辑.


            // 一个服务器, 运行过程中, 要做的事情, 主要是三个核心环节.


            // 1. 读取请求, 并解析
            //new一个用于读取请求的对象,创建此对象就要手动申请好内存空间(不能超过64KB),不像我们我们学过的集合类,内部有自己管理内存的能力
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            //DatagramPacket 是输出型参数,传入receive的对象是空,调用完receive后的空对象的内容就会被填充上
            //如果客户端的请求一直没来,recieve方法就会阻塞等待==>一直阻塞到真的有客户端发送东西来
            socket.receive(requestPacket);



            //把得到的数据报,转化成字符串,这样的转字符串的前提是, 后续客户端发的数据就是一个文本的字符串.getLength是数据报实际长度
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());



            // 2. 根据请求, 计算出响应
            //回显服务器这请求就是响应,因此假装调用下自己写的一个方法
            String response = process(request);



            // 3. 把响应写回给客户端
            // 此时需要告知网卡, 要发的内容是啥(获取response的内部字节数组和长度,长度要是字节长度), 要发给谁(客户端的IP和端口在requestPacket数据报里面).
            //DatagramPacket api都是按照字节来处理的,一定要传字节数组与字节长度(要相互对应)
            //DatagramPacket requestPacket 这个客户端传过来的对象 就包含向客户端通信的ip 和 port
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());//requestPacket.getSocketAddress() 本质也是new了一个InetSocketAddress对象
            socket.send(responsePacket);//用send返回响应数据报



            // 记录日志, 方便观察程序执行效果.
            System.out.printf("[%s:%d] req(请求): %s, resp(响应): %s\n", requestPacket.getAddress().toString(), requestPacket.getPort(),
                    request, response);
        }
    }



    // 根据请求计算响应. 由于是回显程序, 响应内容和请求完全一样.
    public String process(String request) {
        return request;
    }


    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);//端口号随便写的
        server.start();//启动服务器
    }
}
